import 'dart:collection';
import 'dart:convert';

import 'package:xsdk_flutter/xsdk/utils/x_utils_dart.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_log.dart';
import 'package:xsdk_flutter/xsdk/utils/x_utils_string.dart';

/*

注意:
实例变量不用使用Set定义,要改为List
实例变量类型敏感,int类型不能用String类型接收, 如不明类型则可用dynamic类型接收

注意:
如果报错提示dart_Style_2.3.5版本的问题(系统自动直接引用最新?),则该pubspec.lock中对应的dart_Style,改为2.3.4即可

方法1:
使用自带库:dart:convert(快速,易操作,适用任何项目,直接生成最终文件,后期修改需要手动更改,在线:https://javiercbk.github.io/json_to_dart/)
安装android插件: jsonToDart
使用: File -> New -> Json To Dart 或者 文件 -> 右键 ->生成 -> Json To Dart

方法2:
使用json_serializable库(易维护,适用中大型项目,使用命令生成最终文件,后期修改不用手动,重新生成即可)
安装:
dependencies:
  json_annotation: ^4.8.1
dev_dependencies:
  build_runner: ^2.3.3
  json_serializable: ^6.7.1

使用:
1.1.创建user.dart源文件 (手动)
import 'package:json_annotation/json_annotation.dart';
//定义生成的文件
part 'user.g.dart';

//使用@JsonSerializable()或@JsonSerializable(explicitToJson: true)标记需要生成的json实体类
//@JsonSerializable() //内部没有嵌套类时使用
@JsonSerializable(explicitToJson: true) //当内部存在嵌套类时使用,不存在也可以使用
class User {
  User(this.name, this.email);
  String name;

  //json_serializable处理json返回的字段实际名称对应
  @JsonKey(name: 'Email')
  String email;

  //默认值(当JSON没有返回值时,返回null或没有对应的key,json_serializable使用默认值赋值)
  @JsonKey(defaultValue: false)
  final bool isAdult;

  //json_serializable要求JSON必须返回该字段值,必须含有对应的key
  @JsonKey(required: true)
  final String id;

  //json_serializable生成实体类代码忽略该字段
  @JsonKey(ignore: true)
  final String verificationCode;

  //添加固定格式: _$自定义的类名FromJson  _$自定义的类名ToJson
  factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
  Map<String, dynamic> toJson() => _$UserToJson(this);
}

1.2.使用android插件自动生成源文件:
安装android插件:搜索json_serializable,安装AdvancedJson2Dart或者FlutterJsonToDart,
使用: 点击选择文件夹 -> 右键 -> new -> Json2Dart(AdvancedJson2Dart) 或 FlutterJsonToDart,
注意:需要手动更改源码的注解为@JsonSerializable(explicitToJson: true)

2.命令生成(在项目的根目录):
 flutter pub run build_runner build --delete-conflicting-outputs
 或  flutter pub run build_runner watch
(这时之前在user.dart里报错的位置,都正常了,同目录下生成user.g.dart文件,记得添加版本管理上传即可)

如果报错则:
flutter clean
dart run
flutter pub run build_runner build --delete-conflicting-outputs

3.像以前那样使用:
Map<String, dynamic> userMap = jsonDecode(jsonString);
var user = User.fromJson(userMap);
String json = jsonEncode(user);

如果序列化保存json,变量类型改变,如int变成string,则会报错



注意:
当api返回字段类型不一致时(int,String),统一用dynamic接收,
当要保存数据库时,由于数据库类型不支持dynamic,这要换成String?, 那么就要使用 @JsonKey(readValue: _readValueForId)
例:
@JsonSerializable(explicitToJson: true,includeIfNull:false) //当内部存在嵌套类时使用,不存在也可以使用
class XTestBean {
  //从别的字段取值,或重新转类型,计算等 注意:与name的使用有区别
  @JsonKey(readValue: _readValueForId)
  String? id;//

  @JsonKey(readValue: _commonReadValue)
  String? type;//
  @JsonKey(readValue: _commonReadValue)
  String? isUnique;//

  //获取json字符串里的xxx字段,并赋值给对应成员变量,注意toJson时也会更改成xxx新字段值
  @JsonKey(name: 'xxx')
  String? name;//

  XTestBean();

  //添加固定格式: _$自定义的类名FromJson  _$自定义的类名ToJson
  factory XTestBean.fromJson(Map<String, dynamic> json) => _$XTestBeanFromJson(json);
  Map<String, dynamic> toJson() => _$XTestBeanToJson(this);

  //自定义从id或别的字段中取值
  static Object? _readValueForId(Map<dynamic, dynamic> json, String key){
    XLogUtils.printLog('---json=$json,key=$key');
    return '${json['longid']}';
  }

  //同字段名通用
  static Object? _commonReadValue(Map<dynamic, dynamic> json, String key){
    XLogUtils.printLog('---json=$json,key=$key');
    return '${json[key]}';
  }

}

//其他使用:
@JsonSerializable() 注解在class上 代表该类有需要自动生成补全的代码
@JsonSerializable(includeIfNull: false) 当对象作为另一个对象的成员变量使用时，如果值为false，toJson转成字符串时，不生成对应字段
@JsonKey(name: 'xxx') 获取json字符串里的xxx字段,并赋值给对应成员变量
@JsonKey(nullable: false) 同上json字符串中该字段不能为空，如果缺少字段，会运行时异常 nullable: false 的字段在构造函数里的必填的
@JsonKey(includeIfNull: false) 如果值为false，toJson转成字符串时，不生成对应字段
@JsonKey(name: 'date-of-birth', nullable: false) 获取json字符串里的xxx字段,并赋值给对应成员变量，并且json字符串中字段不能为空
注JsonSerializable()注解的类一定要有构造函数，否则转换失败
普通用法1:
只在class添加 @JsonSerializable()注解，字段与json字符串字段要一一对应，json字符串里的key叫什么名字，你的成员属性就要起对应一模一样的名字，并且只支持基本数据类型。

高级用法1:
复杂对象转换: @JsonKey(name: 'prep-time',fromJson: 方法名称,toJson: 方法名称)
@JsonKey(fromJson: 方法名称, toJson: 方法名称)
fromJson json字符串转成对象时用 基本数据类型转成复杂对象
toJson 对象转成字符串时使用  复杂对象转成基本数据类型

高级用法2:
@JsonLiteral('config.json') 根据json文件生成对应map集合,适合做配置文件时使用





 */
abstract class XJsonUtils{

  /*
  (不能转实体类,map的key可能输错)
  自带库:dart:convert json转换map/list,注意:并不能直接转为数据结构模型,还需要另外写一个实体类的fromJson()方法,如
  class User {
  final String name;
  final String email;

  User(this.name, this.email);

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        email = json['email'];

  Map<String, dynamic> toJson() => {
        'name': name,
        'email': email,
      };
}

   */
  static Map<String,dynamic> json2Map(String? jsonString){

    try {

      if(XStringUtils.isEmpty(jsonString)){
        return {};
      }

      return jsonDecode(jsonString!);

    }catch (e) {
      XLogUtils.printLog('---json2Map error: $e');
    }

    return {};

  }
  /*
  list转换成对应的子类:
List<XBaseDevice> connectedDevices=[];
List<dynamic> list=XJsonUtils.json2Object(connectDeviceJson);
for(dynamic item in list){
  connectedDevices.add(XBaseDevice.fromJson(item));
}
   */
  static List<dynamic> json2List(String? jsonString){
    try {
      if(XStringUtils.isEmpty(jsonString)){
        return [];
      }
      return jsonDecode(jsonString!);
    }catch (e) {
      XLogUtils.printLog('json2Object error: $e');
    }
    return [];
  }

  /*
  自带库:dart:convert, obj to json
  注意:
  此obj如果直接传实例对象,那么这个类需要重写有toJson()方法;
  如果该类没有toJson方法,这需要将实例转对应map传入;
  如果是List则item都需要有toJson方法
  如果是Set需要转List,否则报错
   */
  static String obj2Json(Object? objTojson){

    if(objTojson==null){
      return '';
    }

    dynamic obj;
    if('Set'==XDartUtils.getDataItemClassName(objTojson)){
      obj=(objTojson as Set).toList();
    }else if('Queue'==XDartUtils.getDataItemClassName(objTojson)){
      obj=(objTojson as Queue).toList();
    }else if('Map'==XDartUtils.getDataItemClassName(objTojson)){
      //去除null的字段
      (objTojson as Map).removeWhere((key, value) => value==null);
      obj=objTojson;
    }else if('String'==XDartUtils.getDataItemClassName(objTojson)){
      //XLogUtils.printLog('---objTojson is String');
      return objTojson.toString();
    }else{
      //List,Uint8List等支持
      obj=objTojson;
    }
    //
    var json = jsonEncode(obj);
    return json;
  }

}
